#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;


////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
    this->nb_Erreur =0;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
        if (this->nb_Erreur == 0){
                cout << "Syntaxe correcte." << endl;
        }
        else{
                cout << nb_Erreur << " Erreurs détectés." << endl;
        }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
        // on crée un noeud et on l'affecte à la fonction que l'on veut executer
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
        // on renvoie la valeur du noeud
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }
         // on crée un noeud
	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="si" 
              || ls.getSymCour()=="tantque"    || ls.getSymCour()=="repeter"
              || ls.getSymCour()=="pour"       || ls.getSymCour()=="lire"
              || ls.getSymCour()=="ecrire"     || ls.getSymCour() == "selon");
        // on execute la sequence tant que on est sur le mot-clé donc l'instruction qui concerne
        // on renvoie la valeur du noeud
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instPour> | lire | ecrire
// on teste chaque cas grâce à son mot-clé qui le définit et on retourne la fonction qui lui est associé
// on englobe le tout dans un try pui on fait un catch pour récupérer après une erreur afin de continuer l'analyse et éventuellement détecter d'autres erreurs de syntaxe

    try 
        { 
        if (ls.getSymCour()=="<VARIABLE>")
            return affectation();
        
        else if (ls.getSymCour()=="si")
            return instSi();

        else if (ls.getSymCour()=="tantque")  
            return instTq();

        else if (ls.getSymCour()=="repeter")
            return instRepeter();
            
        else if (ls.getSymCour()=="pour")
            return instPour();
        
        else if (ls.getSymCour()=="lire")
            return instLire();
        
        else if (ls.getSymCour()=="ecrire")
            return instEcrire();
        
        else if (ls.getSymCour() == "selon")
                return instSelon();
            
        else{
            erreur("instruction");
            return 0;
        }
        }
catch (exception const& e) 
        {
        nb_Erreur++;
        while (ls.getSymCour() != ";"){
                ls.suivant();}
        return(0);
        }
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>
        // on crée le noeud pour l'expression
        Noeud* exp = NULL;

	testerSymCour("<VARIABLE>");
        // on crée le noeud pour la variable, qu'on ajoute en meme temps à la table
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
        if (ls.getSymCour() == "="){
            ls.suivant();
            exp= expression();
        }
        else if (ls.getSymCour() == "+="||ls.getSymCour() == "-="||ls.getSymCour() == "/="||ls.getSymCour() == "*="||ls.getSymCour() == "%=" ){
            Symbole op = ls.getSymCour();
            ls.suivant();
            exp = new NoeudOperateurBinaire(op,var,expression());
        }
        
        else if (ls.getSymCour() == "++"||ls.getSymCour() == "--"){
            Symbole op = ls.getSymCour();
            ls.suivant();
            exp = new NoeudOperateurBinaire(op,var,NULL);
        }
        // on construit un noeud qui est racine des deux noeuds crées précedemment
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }
        // on crée un noeud
	Noeud* ter = terme();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		Symbole operateur = opAdd();                               // on stocke le symbole de l'opérateur
		Noeud* termeDroit=terme();                                 // lecture de l'operande droit
		ter = new NoeudOperateurBinaire(operateur,ter,termeDroit); // const. du noeud
	}
        // on construit le noeud
	return ter;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier>  |  <variable>  |  <opUnaire> <expBool>  |  ( <expBool> )
        // on crée un noeud
	Noeud* fact = NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") 
        {
		fact = ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} 
        
        else if (ls.getSymCour()=="-") 
        {
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
                Symbole op = opUnaire();
		fact = new NoeudOperateurBinaire(op, NULL, expBool());
	} 
        
        else if (ls.getSymCour()=="non")
        {
            Symbole op = opUnaire();
            fact = new NoeudOperateurBinaire(op, NULL, expBool());
        }
        
        else if (ls.getSymCour()=="(") 
        {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	} 
        
        else
		erreur("<facteur>");
        // on construit le noeud
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
                // on lève l'exception quand il y a une erreur
                throw Ex();
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
        // on lève l'exception quand il y a une erreur
        throw Ex();
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme()
// <terme> ::= <facteur> { <opMult> <facteur> }
{
    // on crée le noeud
    Noeud* fact = facteur();
    while (ls.getSymCour()=="*" || ls.getSymCour()=="/")
        {
                Symbole operateur = opMult(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=facteur(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
        }
    // on construit le noeud
    return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | -
{
        // on crée le symbole pour l'operateur addition
        Symbole add;
    	if (ls.getSymCour()=="+" || ls.getSymCour()=="-")
        {
            add=ls.getSymCour();
            ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opAdd>");
        // on construit le symbole
        return add;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::= * | / 
{
        // on crée le symbole pour l'operateur multiplication
        Symbole mult;
    	if (ls.getSymCour()=="*" || ls.getSymCour()=="/")
        {
                mult=ls.getSymCour();
		ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opMult>");
        // on construit le symbole
        return mult;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation()
// <relation> ::= <expression> { <opRel> <expression> }
{
        // on crée un noeud
        Noeud* exp = expression();
    	while (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || 
               ls.getSymCour()=="<"  || ls.getSymCour()=="<=" || 
               ls.getSymCour()==">"  || ls.getSymCour()==">=")
        {
		Symbole operateur = opRel(); // on stocke le symbole de l'opérateur
		Noeud* expDroit=expression(); // lecture de l'operande droit
		exp = new NoeudOperateurBinaire(operateur,exp,expDroit); // const. du noeud
        }
        // on construit le noeud
        return exp;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel(){
// <opRel> ::= == | != | < | <= | > | >=
    
        // on crée le symbole pour l'operateur de relation
        Symbole rel;
    	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || 
            ls.getSymCour()=="<"  || ls.getSymCour()=="<=" || 
            ls.getSymCour()==">"  || ls.getSymCour()==">=")
        {
                rel = ls.getSymCour();
		ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opRel>");
        // on construit le symbole
        return rel;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire()
// <opUnaire> ::= - | non
{
        // on crée le symbole pour l'operateur unaire
        Symbole unaire;
    	if (ls.getSymCour()=="-" || ls.getSymCour()=="non")
        {
                unaire = ls.getSymCour();
		ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opUnaire>");
        // on construit le symbole
        return unaire;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi()
// <instSi> ::= si ( <expBool> ) <seqInst> break { sinonsi ( <expBool> ) <seqInst> break} [ sinon <seqInst> break] finsi
{
        // on crée le noeud
        NoeudInstSi* iS = new NoeudInstSi();
        sauterSymCour("si");
        sauterSymCour("(");
        iS->ajouterExpBool(expBool());
        sauterSymCour(")");
        iS->ajouterSeqInst(seqInst());
        
        while (ls.getSymCour()=="sinonsi")
        {
                ls.suivant();
                sauterSymCour("(");
                iS->ajouterExpBool(expBool());
                sauterSymCour(")");
                iS->ajouterSeqInst(seqInst());
        }
        
        if (ls.getSymCour()=="sinon")
        {
                ls.suivant();
                iS->ajouterExpBool(NULL);
                iS->ajouterSeqInst(seqInst());
        }
            
        sauterSymCour("finsi");
        // on construit le noeud
        return iS;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq()
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
{
        sauterSymCour("tantque");
        sauterSymCour("(");
        // on cree le noeud pour l'expression booléenne
	Noeud* exp = expBool();
	sauterSymCour(")");
        // on crée le noeud pour la sequence d'instruction
        Noeud* seq = seqInst();
	sauterSymCour("fintantque");
        // on construit un noeud qui est racine des deux noeuds crées précedemment
        return new NoeudInstTq(exp, seq);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )        
{
        sauterSymCour("repeter");
        // on crée le noeud pour la sequence d'instruction
        Noeud* seq = seqInst();
	sauterSymCour("jusqua");
        sauterSymCour("(");
        // on cree le noeud pour l'expression booléenne
	Noeud* exp = expBool();
	sauterSymCour(")");
        // on construit un noeud qui est racine des deux noeuds crées précedemment
        return new NoeudInstRepeter(exp,seq);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour()    
// <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation>) <seqInst> finpour
{
        sauterSymCour("pour");
        sauterSymCour("(");
        // on crée le noeud pour la premiere affectation
	Noeud* aff1 = affectation();
        sauterSymCour(";");
        // on crée le noeud pour l'expression booléenne
        Noeud* exp = expBool();
        sauterSymCour(";");
        // on crée le noeud pour la deuxieme affectation
        Noeud* aff2 = affectation();
	sauterSymCour(")");
        Noeud* seq = seqInst();
        sauterSymCour("finpour");
        // on construit un noeud qui est racine des trois noeuds crées précedemment
        return new NoeudInstPour(aff1,exp,aff2,seq);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire()
// <instLire> ::= lire ( <variable> )   
{
        sauterSymCour("lire");
        sauterSymCour("(");
        // on teste d'abord si c'est vraiment une variable et on crée le noeud qu'on ajoute en meme temps à la table
        testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour(")");
        // on construit un noeud qui est racine du noeud crée précedemment
        return new NoeudInstLire(var);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire()
// <instEcrire> ::= ecrire ( <expression> | <chaine> ) 
{
        sauterSymCour("ecrire");
        sauterSymCour("(");
        // on crée le noeud
        Noeud* exp = NULL;
        if (ls.getSymCour()=="<CHAINE>") 
        {
                testerSymCour("<CHAINE>");
                // on ajoute à la table l'expression
                exp = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
        }
        
        else 
        {
                 exp = expression();
        }        
	sauterSymCour(")");
        // on construit un noeud qui est racine du noeud crée précedemment
        return new NoeudInstEcrire(exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool()    
// <expBool> ::= <termeBool> { <opOu> <termeBool> }
{
        // on crée le noeud
        Noeud* tb = termeBool();
    	while (ls.getSymCour()=="ou")
        {
                Symbole operateur = opOu(); // on stocke le symbole de l'opérateur
		Noeud* tbDroit=termeBool(); // lecture de l'operande droit
		tb = new NoeudOperateurBinaire(operateur,tb,tbDroit); // const. du noeud
        }
        // on construit le noeud
        return tb;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::termeBool()
// <termeBool> ::= <relation> { <opEt> <relation> }
{
    // on crée le noeud
    Noeud* rel = relation();
    while (ls.getSymCour()=="et")
        {
                Symbole operateur = opEt(); // on stocke le symbole de l'opérateur
		Noeud* relDroit=relation(); // lecture de l'operande droit
		rel = new NoeudOperateurBinaire(operateur,rel,relDroit); // const. du noeud
        }
    // on construit le noeud
    return rel;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt()
// <opEt> ::= et 
{
        // on crée le symbole
        Symbole e;
    	if (ls.getSymCour()=="et")
        {
            e = ls.getSymCour();
		ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opEt>");
        // on construit le symbole
        return e;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu()
// <opOu> ::= ou 
{
        // on crée le symbole
        Symbole o;
    	if (ls.getSymCour()=="ou")
        {
                o = ls.getSymCour();
		ls.suivant();
        }
        // on teste le cas où ca marche pas et on met une erreur
	else
		erreur("<opOu>");
        // on construit le symbole
        return o;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSelon() 
{
// <instSelon> ::= selon (<expression> | <ENTIER>) cas <expression> | <ENTIER>:<seqInst>{ cas <expression> | <ENTIER>:<seqInst> } defaut:<seqInst> finselon
        // on declare le noeud
        NoeudInstSelon* iSw;
        sauterSymCour("selon");
        sauterSymCour("(");
        // on teste d'abord si c'est vraiment une variable et on crée le noeud qu'on ajoute en meme temps à la table
        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        // on crée le noeud
        iSw = new NoeudInstSelon(var);

        while (ls.getSymCour() == "cas") {
            ls.suivant();
            // on teste d'abord si c'est vraiment un entier et on crée le noeud qu'on ajoute en meme temps à la table
            if (ls.getSymCour() == "<ENTIER>") {
                Noeud* ent = ts.chercheAjoute(ls.getSymCour());
                iSw->ajouterExpBool(ent);
                ls.suivant();
            }
                sauterSymCour(":");
                iSw->ajouterSeqInst(seqInst());
        }

        if (ls.getSymCour() == "defaut") {
                ls.suivant();
                iSw->ajouterExpBool(NULL);
                sauterSymCour(":");
                iSw->ajouterSeqInst(seqInst());
        }
        sauterSymCour("finselon");
        // on construit le noeud
        return iSw;
}   
